package cn.outsourcing.supermarket.controller;

import cn.felord.payment.wechat.enumeration.TradeState;
import cn.felord.payment.wechat.v3.WechatApiProvider;
import cn.felord.payment.wechat.v3.WechatMarketingFavorApi;
import cn.felord.payment.wechat.v3.WechatPayCallback;
import cn.felord.payment.wechat.v3.model.ResponseSignVerifyParams;
import cn.outsourcing.supermarket.common.constants.PayConstants;
import cn.outsourcing.supermarket.common.utils.DataUtils;
import cn.outsourcing.supermarket.common.enums.OrderTypeEnum;
import cn.outsourcing.supermarket.po.HiolabsOrder;
import cn.outsourcing.supermarket.po.HiolabsUser;
import cn.outsourcing.supermarket.service.*;
import cn.outsourcing.supermarket.vo.OrderGoodsVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 注意为了演示该配置在使用微信配置application-wechat.yaml才生效
 *
 * <p>务必保证回调接口的幂等性
 *
 * <p>微信回调控制器，当支付成功、代金券核销成功后，微信支付服务器会通过回调进行通知商户侧。 商户侧可以根据微信的回调通知进行支付的后续处理，例如支付状态的变更等等。
 * 需要注意的是回调接口需要白名单放行。
 *
 * <p>开发者只需要编写对结果的{@link java.util.function.Consumer}即可。
 *
 * <p>请注意：返回的格格式必须是{@link WechatPayCallback} 给出的格式，不能被包装和更改，切记！
 *
 * @author felord.cn
 * @since 1.0.0.RELEASE
 */
@Profile({"wechat"})
@RestController
@Slf4j
@RequestMapping("/wxpay/callbacks")
public class CallbackController {

  String TENANT_ID = "mobile";
  @Resource private WechatApiProvider wechatApiProvider;
  @Resource private HiolabsOrderService hiolabsOrderService;
  @Resource private HiolabsUserService hiolabsUserService;
  @Resource private HiolabsGoodsService goodsService;
  @Resource private HiolabsOrderGoodsService orderGoodsService;
  @Resource private HiolabsProductService productService;

  /**
   * 代金券核销通知.
   *
   * <p>需要手动调用{@link WechatMarketingFavorApi#setMarketingFavorCallback(String)} 设置，一次性操作!
   *
   * @param wechatpaySerial the wechatpay serial
   * @param wechatpaySignature the wechatpay signature
   * @param wechatpayTimestamp the wechatpay timestamp
   * @param wechatpayNonce the wechatpay nonce
   * @param request the request
   * @return the map
   */
  @SneakyThrows
  @PostMapping("/coupon")
  public Map<String, ?> couponCallback(
      @RequestHeader("Wechatpay-Serial") String wechatpaySerial,
      @RequestHeader("Wechatpay-Signature") String wechatpaySignature,
      @RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
      @RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
      HttpServletRequest request) {
    String body = request.getReader().lines().collect(Collectors.joining());
    // 对请求头进行验签 以确保是微信服务器的调用
    ResponseSignVerifyParams params = new ResponseSignVerifyParams();
    params.setWechatpaySerial(wechatpaySerial);
    params.setWechatpaySignature(wechatpaySignature);
    params.setWechatpayTimestamp(wechatpayTimestamp);
    params.setWechatpayNonce(wechatpayNonce);
    params.setBody(body);
    return wechatApiProvider
        .callback(TENANT_ID)
        .couponCallback(
            params,
            data -> {

              // TODO 对回调解析的结果进行消费  需要保证消费的幂等性 微信有可能多次调用此接口
            });
  }

  /**
   * 微信支付成功回调.
   *
   * <p>无需开发者判断，只有扣款成功微信才会回调此接口
   *
   * @param wechatpaySerial the wechatpay serial
   * @param wechatpaySignature the wechatpay signature
   * @param wechatpayTimestamp the wechatpay timestamp
   * @param wechatpayNonce the wechatpay nonce
   * @param request the request
   * @return the map
   */
  @SneakyThrows
  @PostMapping("/transaction")
  public Map<String, ?> transactionCallback(
      @RequestHeader("Wechatpay-Serial") String wechatpaySerial,
      @RequestHeader("Wechatpay-Signature") String wechatpaySignature,
      @RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
      @RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
      HttpServletRequest request,
      HttpServletResponse response) {
    String body = request.getReader().lines().collect(Collectors.joining());
    // 对请求头进行验签 以确保是微信服务器的调用
    ResponseSignVerifyParams params = new ResponseSignVerifyParams();
    params.setWechatpaySerial(wechatpaySerial);
    params.setWechatpaySignature(wechatpaySignature);
    params.setWechatpayTimestamp(wechatpayTimestamp);
    params.setWechatpayNonce(wechatpayNonce);
    params.setBody(body);
    return wechatApiProvider
        .callback(TENANT_ID)
        .transactionCallback(
            params,
            data -> {
              try {
                log.info("支付回调data:{}", data);
                String outTradeNo = data.getOutTradeNo();
                QueryWrapper<HiolabsOrder> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("order_sn", outTradeNo);
                HiolabsOrder one = hiolabsOrderService.getOne(queryWrapper);
                if (Objects.equals(data.getTradeState(), TradeState.SUCCESS)) {
                  if (Objects.nonNull(one)) {
                    Integer userId = one.getUserId();
                    HiolabsUser byId = hiolabsUserService.getById(userId);
                    BigDecimal actualPrice = one.getActualPrice();
                    // FIXME：使用积分后需要对用户进行积分扣除。
                    BigDecimal integral =
                        Optional.ofNullable(actualPrice)
                            .map(e -> e.divide(BigDecimal.valueOf(10), 2, RoundingMode.HALF_UP))
                            .orElse(BigDecimal.ZERO);
                    // TODO：计算使用优惠卷后的价格
/*                    if ( 使用优惠卷) {
                      actualPrice = actualPrice.subtract(优惠卷优惠价格);
                    }*/
                    // 应扣除的积分
                    BigDecimal shouldSubtractIntegral =
                        DataUtils.getUsedIntegral(one.getOrderPrice(), actualPrice);
                    byId.setCredits(
                        byId.getCredits().add(integral).subtract(shouldSubtractIntegral));
                    //如果是充值订单，则为账号添加余额
                    if (Objects.equals(one.getOrderType(), OrderTypeEnum.TOP_UP_BALANCE.getIndex())) {
                      BigDecimal balance = Optional.ofNullable(one.getActualPrice()).orElse(BigDecimal.ZERO);
                      byId.setBalance(balance.add(byId.getBalance()));
                    }
                    //                        byId.setCredits(byId.getCredits().add(integral));
                    hiolabsUserService.updateById(byId);
                    one.setOrderStatus(PayConstants.PAYMENT_HAS_BEEN);
                    one.setPayStatus(true);
                    one.setPayTime(DataUtils.getSeconds());
                    one.setPayId(data.getTransactionId());
                    // 操作销量与库存
                    List<OrderGoodsVO> orderGoods = orderGoodsService.getOrderGoods(one.getId());
                    for (OrderGoodsVO orderGood : orderGoods) {
                      goodsService.reduceStock(orderGood.getGoodsId(), orderGood.getNumber());
                      productService.reduceStock(one.getId(), orderGood.getNumber());
                    }
                  }
                  response
                      .getOutputStream()
                      .println(
                          "<xml><return_code><![CDATA["
                              + TradeState.SUCCESS
                              + "]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
                  log.info("支付成功,outTradeNo:{},state:{}", outTradeNo, TradeState.SUCCESS);
                } else {
                  if (Objects.nonNull(one)) {
                    one.setOrderStatus(PayConstants.NOT_PAYMENT);
                    one.setPayStatus(true);
                  }
                  response
                      .getOutputStream()
                      .println(
                          "<xml><return_code><![CDATA["
                              + data.getTradeState()
                              + "]]></return_code><return_msg><![CDATA[ERROR]]></return_msg></xml>");
                  log.info("支付成功,outTradeNo:{},state:{}", outTradeNo, data.getTradeState());
                }
                hiolabsOrderService.updateById(one);
                response.flushBuffer();
                return;
              } catch (IOException e) {
                e.printStackTrace();
              }
              // TODO 对回调解析的结果进行消费  需要保证消费的幂等性 微信有可能多次调用此接口
            });
  }

  /**
   * 微信合单支付成功回调.
   *
   * <p>无需开发者判断，只有扣款成功微信才会回调此接口
   *
   * @param wechatpaySerial the wechatpay serial
   * @param wechatpaySignature the wechatpay signature
   * @param wechatpayTimestamp the wechatpay timestamp
   * @param wechatpayNonce the wechatpay nonce
   * @param request the request
   * @return the map
   */
  @SneakyThrows
  @PostMapping("/combine_transaction")
  public Map<String, ?> combineTransactionCallback(
      @RequestHeader("Wechatpay-Serial") String wechatpaySerial,
      @RequestHeader("Wechatpay-Signature") String wechatpaySignature,
      @RequestHeader("Wechatpay-Timestamp") String wechatpayTimestamp,
      @RequestHeader("Wechatpay-Nonce") String wechatpayNonce,
      HttpServletRequest request) {
    String body = request.getReader().lines().collect(Collectors.joining());
    // 对请求头进行验签 以确保是微信服务器的调用
    ResponseSignVerifyParams params = new ResponseSignVerifyParams();
    params.setWechatpaySerial(wechatpaySerial);
    params.setWechatpaySignature(wechatpaySignature);
    params.setWechatpayTimestamp(wechatpayTimestamp);
    params.setWechatpayNonce(wechatpayNonce);
    params.setBody(body);
    return wechatApiProvider
        .callback(TENANT_ID)
        .combineTransactionCallback(
            params,
            data -> {
              // TODO 对回调解析的结果进行消费  需要保证消费的幂等性 微信有可能多次调用此接口
            });
  }
}
